%% @author Carlos Abalde <carlos.abalde@gmail.com>
%% @doc    ...

%%%----------------------------------------------------------------------
%%%
%%% EMC, Copyright (C) 2009 Carlos Abalde
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------

-module(emc.connection).
-behaviour(gen_server).

-import(lists).
-import(io_lib).
-import(gen_server).
-import(erlang).
-import(emc.util.config).
-import(emc.util.util).

-include("emc.hrl").

-define(TIMEOUT, 60000).

%% API
-export([open/2, close/1, execute/3]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(state, { port }).

%%%-----------------------------------------------------------------------------
%%% API
%%%-----------------------------------------------------------------------------

%%
%%
%%
open(Hosts, Behavior) ->
    gen_server:start_link(?MODULE, [Hosts, Behavior], []).

%%
%%
%%
execute(ConnPid, Command, Timeout) ->
    gen_server:call(ConnPid, {execute, Command, Timeout}, infinity).

%%
%%
%%
close(ConnPid) ->
    gen_server:cast(ConnPid, {close}),
    ok.

%%%-----------------------------------------------------------------------------
%%% gen_server callbacks
%%%-----------------------------------------------------------------------------

%%
%%
%%
init([Hosts, Behavior]) ->
    ?INFO(emc, io_lib:format("Initializing port connection", [])),
    {ok, Port} = start_port(Hosts, Behavior),
    link(Port),
    {ok, #state{ port = Port }}.

%%
%%
%%
handle_call({execute, Command, Timeout}, _, State) ->
    case catch call_port(State#state.port, Command, Timeout) of
	{'EXIT', Reason} -> {stop, Reason, {error, Reason}, State};
        Reply            -> {reply, Reply, State}
    end;

handle_call(_, _, State) ->
    {reply, unknown_request, State}.

%%
%%
%%
handle_cast({close}, State) ->
    {stop, normal, State};

handle_cast(_, State) ->
    {noreply, State}.

%%
%%
%%
handle_info(_, State) ->
    {noreply, State}.

%%
%%
%%
terminate(_, State) ->
    catch stop_port(State#state.port),
    ok.

%%
%%
%%
code_change(_, State, _) ->
    {ok, State}.

%%%-----------------------------------------------------------------------------
%%% Internal implementation
%%%-----------------------------------------------------------------------------

%%
%%
%%
start_port(Hosts, Behavior) ->
    Port = open_port({spawn, config:get('emc.root_dir') ++ "/priv/bin/" ++ config:get('emc.port.binary')}, [{packet, 2}, binary]),
    ok = call_port(Port, {init, Hosts, Behavior}, ?TIMEOUT),
    {ok, Port}.

%%
%%
%%
call_port(Port, Command, Timeout) ->
    Port ! {self(), {command, term_to_binary(Command)}},
    receive
        {Port, {data, Data}} -> binary_to_term(Data)
    after
        Timeout -> exit(timeout)
    end.

%%
%%
%%
stop_port(Port) ->
    F = fun() ->
		Port ! {self(), close},
		receive
		    {Port, closed} -> ok
		after
		    ?TIMEOUT -> catch exit(Port)
		end
	end,
    spawn(F),
    ok.
